package inutan.gateway.inner;

import inutan.common.constant.DataConstant;
import inutan.common.context.FlowContext;
import inutan.common.inner.protocol.InnerPing;
import inutan.gateway.action.GatewayHandler;
import inutan.gateway.external.ExternalKit;
import inutan.common.inner.protocol.InnerMessage;
import inutan.gateway.session.LogicSessions;
import inutan.gateway.session.user.UserSession;
import inutan.gateway.session.user.UserSessions;
import org.ricks.common.TaskBus;
import org.ricks.common.exception.ServerBootstrapException;
import org.ricks.common.lang.Logger;
import org.ricks.ioc.RicksContext;
import org.ricks.ioc.definition.wrap.ActionMethodWrapper;
import org.ricks.net.*;
import org.ricks.net.handler.AbstractMessageProcessor;
import org.ricks.net.rpc.AskPackage;

import java.io.IOException;
import java.util.Objects;

/**
 * @author chenwei
 * @Description: 玩家消息 处理handler
 * 1、维护玩家session
 * 2、维护不同模块节点session
 * 3、转发给其它模块节点
 * 4、转发给玩家 （to 1, to N, to all ）
 * @date 2023/2/1510:40
 */
public class InnerHandler extends AbstractMessageProcessor<Object> {

    @Override
    public void process0(AioSession session, Object obj) {
        /**
         * broker玩家消息 处理handler 不需要玩家路由 action
         * 直接cmd 进行转发模块，避免功能开发繁琐
         * 需要根据cmd 做不同的处理
         * 尽量简化 broker 指令 职责，做好一次开发以后尽量不用动
         */
        if(obj instanceof InnerMessage msg) {
            if (msg.state == DataConstant.RUTE_MESSAGE) {
                ActionMethodWrapper methodDefinition = RicksContext.me().getActionMethod(msg.cmd);
                if (Objects.isNull(methodDefinition)) {
                    UserSession userSession = UserSessions.me().getUserSessionBySessionId(msg.userSessionId);
                    LogicSessions.me().route(msg, userSession); //转发
                    return;
                }
                DefaultPacket defaultPacket = new DefaultPacket(msg.cmd, msg.data);
                FlowContext flowContext = new FlowContext(session, defaultPacket);
                flowContext.setUserId(msg.userId);
                flowContext.setPackageId(msg.packageId);
                flowContext.setUserSessionId(msg.userSessionId);
//                flowContext.setGroupId(msg.groupId);
                flowContext.setToUserIds(msg.toUserIds);
                methodDefinition.invoke(flowContext);
            } else {
//                Logger.info("网关收到业务节点，广播给客户端的消息。cmd =" + msg.getCmd() + " , meger = " + msg.packageId + " , statue = " + msg.responseStatus);
//                Logger.info(" packId = " + Test.getPackageId(msg.packageId) + " & mid = " + Test.getMid(msg.getPackageId()));
                ExternalKit.broadcast(msg); //广播消息
            }
        } else if(obj instanceof InnerPing ping) {
            session.send(ping);
        } else if(obj instanceof AskPackage askPackage) {
            Router.me().receive(session, askPackage);
        }  else {
            throw new ServerBootstrapException("未知消息类型，obj:" + obj);
        }
    }


    @Override
    public void stateEvent0(AioSession session, StateMachineEnum stateMachineEnum, Throwable throwable) {
        switch (stateMachineEnum) {
            case NEW_SESSION: {
                try {
                    //逻辑节点 session 进行管理
                    SessionManager.me().addClientSession(session);
                    //询问逻辑节点 是属于什么逻辑模块
                    TaskBus.execute(session, () -> GatewayHandler.askLogicMark(session));
                    Logger.info("[LOGIC] Create Session: "+ session +" -- " + session.getRemoteAddress());
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
                break;
            }
            case PROCESS_EXCEPTION:
                Logger.error("process exception", throwable);
                break;
            case SESSION_CLOSED:
                try {
                    SessionManager.me().remove(session);
                    SessionManager.me().removeClientSession(session);
                    Logger.info("[LOGIC] Close Session: "+ session +" -- " + session.getRemoteAddress());
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
                break;
            case DECODE_EXCEPTION: {
                Logger.warn("aio decode exception,", throwable);
                break;
            }
        }
    }
}
